package org.wattdepot.modbus.meter.shark;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.InetAddress;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.wimpi.modbus.net.ModbusTCPListener;
import net.wimpi.modbus.procimg.SimpleProcessImage;
import net.wimpi.modbus.procimg.SimpleRegister;
import net.wimpi.modbus.core.ModbusCoupler;
import org.wattdepot.resource.sensordata.jaxb.SensorData;
import org.wattdepot.simulation.dorm.Dorm;
import org.wattdepot.simulation.basic.BitTimeManipulation;
import org.wattdepot.simulation.basic.SystemTime;
import org.wattdepot.simulation.core.Simulation;
import org.wattdepot.client.WattDepotClient;
import javax.swing.Timer;

/**
 * A TCP/IP server program hold particular data such as power, date, time, and energy usage. The
 * server is automated to update every second.
 * 
 * @author Alan Lee
 * 
 */
public class SharkSlave {
  /**Pattern to match IP address.*/
  private static final String IPADDRESS_PATTERN = 
    "^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." +
    "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." +
    "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." +
    "([01]?\\d\\d?|2[0-4]\\d|25[0-5])$";
  
  /**
   * Main program to run Mod-Bus automation.
   * 
   * @param args not applicable
   */
  public static void main(String[] args) {
    
      final Simulation m_Random = new Dorm();
      final BitTimeManipulation multipleValue = new BitTimeManipulation();
      final WattDepotClient client;
      //Reminder of application usage.
      if (args.length != 3) {
        System.out.println("To start: " +
          "java -jar wattdepot-sensor-modbus.meter.1.0.512.jar <Source IP> <Port> <Server IP>\n");
        System.out.println("Requires 3 arguments: You entered " + args.length + "\n");
        System.exit(0);
      }
      try {
        final String hostUri = args[2];       
      
        //Test WattDepot server connection
        client = new WattDepotClient(hostUri);
        if (!client.isHealthy()) {
          System.out.println("<Server IP>: Failed to connect to " + client.getWattDepotUri());
          return;
        }
        
        // Indicate successful connection.
        System.out.println("Connected to " + client.getWattDepotUri());
        
        //Retrieve latest energy usage from WattDepot.
        SensorData serverData = client.getLatestSensorData("SIM_SHARK");
        double latestEnergy = Double.parseDouble(serverData.getProperty("energyConsumedToDate"));
        int data = (int)(latestEnergy / 100);
        m_Random.setServerEnergy(data);
      }
      catch (Exception ex) {
        ex.printStackTrace();
        System.exit(0);
      }
      
      // Important instances and variables
      ModbusTCPListener listener = null;
      final SimpleProcessImage spi = new SimpleProcessImage();
      
            
      // 1. Set port number and address
      try {
        if (args[0] == null) {
          System.out.println("<Source IP>: IP address not entered.\n");
          System.exit(0);
        }
        else if (args[1] == null) {
          System.out.println("<Port>: Port not entered.\n");
          System.exit(0);
        }
        String ipaddr = args[0];
        Pattern regx = Pattern.compile(IPADDRESS_PATTERN);
        Matcher match = regx.matcher(ipaddr);
        if (!match.find()) {
          System.err.println("Invalid IP address.  Check your IP address.  Good-Bye.");
          return;
        }
 
        InetAddress addr = InetAddress.getByName(ipaddr);
      
        int port = Integer.parseInt(args[1]); //new default port for testing.
        
        
        // 2. Create a listener with 3 threads in pool
        listener = new ModbusTCPListener(3);
        listener.setPort(port);
        listener.setAddress(addr);
        listener.start();
      }
      catch (Exception ex) {
        ex.printStackTrace();
      }

      // 3. Prepare a process image
      final SimpleRegister[] test = new SimpleRegister[20000];
      int count = 0;

      while (count < test.length) {
        test[count] = new SimpleRegister(0);
        spi.addRegister(test[count]);
        count++;
      }

      // Debugging
      System.out.println("Debugging Initial Register[0] = " + spi.getRegister(0).getLowValue());

      // 4. Set the image on the coupler
      ModbusCoupler.getReference().setProcessImage(spi);
      ModbusCoupler.getReference().setMaster(false);
      ModbusCoupler.getReference().setUnitID(17);
      
      
      System.out.println("Modbus Slave activated...");
      int delay = 1000; // milliseconds
  try {
      ActionListener taskPerformer = new ActionListener() {

        public void actionPerformed(ActionEvent evt) {
          final SystemTime clock = new SystemTime();
          test[9427].setLowValue(multipleValue.combineMultipleValue((clock.getYear() % 100), 
            clock.getMonth()));
          test[9428].setLowValue(multipleValue.combineMultipleValue(clock.getDay(), 
            clock.getHour()));
          test[9429].setLowValue(multipleValue.combineMultipleValue(clock.getMinute(), 
            clock.getSecond()));          
          // Get instantaneous power      
          m_Random.setIntBits(m_Random.createRandomValue());
          Float power = Float.intBitsToFloat(m_Random.getIntBits());
          m_Random.setIntBits(power / 10);
          test[9018].setHighValue(m_Random.getIntBits());
          test[9019].setLowValue(m_Random.getIntBits());    
          // Get new energy total
          m_Random.totalAccumulatedEnergy((test[9018].getHighValue() + test[9019].getLowValue()));
          Float totalEnergy = Float.intBitsToFloat(m_Random.getEnergyBits());
          m_Random.setEnergyBits(totalEnergy);
          test[1506].setHighValue(m_Random.getEnergyBits());
          test[1507].setLowValue(m_Random.getEnergyBits());
          System.out.println("-------------------" + "-------------------");
          int counter = 9427;
          while (counter <= 9429) {
            System.out.println("Register[" + counter + "] = "
                + spi.getRegister(counter).getLowValue());
            counter++;
          }
          System.out.println("Register[9018] = "
              + Float.intBitsToFloat(spi.getRegister(9018).getHighValue()) + "W");
          System.out.println("Register[9019] = "
              + Float.intBitsToFloat((spi.getRegister(9019).getLowValue())) + "W");
          System.out.println("Register[1506] = " 
              + Float.intBitsToFloat(spi.getRegister(1506).getHighValue()) + "Wh");
          System.out.println("Register[1507] = " 
              + Float.intBitsToFloat(spi.getRegister(1507).getLowValue()) + "Wh");
        }
      };
      new Timer(delay, taskPerformer).start();
    }
    catch (Exception ex) {
      ex.printStackTrace();
    }
  }
}
